/*
                                                                            
  Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or 
  its subsidiaries.                                                         
                                                                            
  Licensed under the Apache License, Version 2.0 (the "License");           
  you may not use this file except in compliance with the License.          
  You may obtain a copy of the License at                                   
                                                                            
     http://www.apache.org/licenses/LICENSE-2.0                             
                                                                            
  Unless required by applicable law or agreed to in writing, software       
  distributed under the License is distributed on an "AS IS" BASIS,         
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  See the License for the specific language governing permissions and       
  limitations under the License.                                            

*/
                                                                            

#include "SwitchBCM81724.h"

#include "swss/logger.h"
#include "meta/sai_serialize.h"

using namespace saivs;

SwitchBCM81724::SwitchBCM81724(
        _In_ sai_object_id_t switch_id,
        _In_ std::shared_ptr<RealObjectIdManager> manager,
        _In_ std::shared_ptr<SwitchConfig> config):
    SwitchStateBase(switch_id, manager, config)
{
    SWSS_LOG_ENTER();

    // empty
}

SwitchBCM81724::~SwitchBCM81724()
{
    SWSS_LOG_ENTER();
}

sai_status_t SwitchBCM81724::create_port_dependencies(
        _In_ sai_object_id_t port_id)
{
    SWSS_LOG_ENTER();

    SWSS_LOG_WARN("check attributes and set, FIXME");

    // this method is post create action on generic create object

    sai_attribute_t attr;

    // default admin state is down as defined in SAI

    attr.id = SAI_PORT_ATTR_ADMIN_STATE;
    attr.value.booldata = false;

    CHECK_STATUS(set(SAI_OBJECT_TYPE_PORT, port_id, &attr));

    return SAI_STATUS_SUCCESS;
}

sai_status_t SwitchBCM81724::initialize_default_objects()
{
    SWSS_LOG_ENTER();

    CHECK_STATUS(set_switch_default_attributes());
    CHECK_STATUS(create_default_trap_group());

    return SAI_STATUS_SUCCESS;
}


sai_status_t SwitchBCM81724::create_qos_queues_per_port(
        _In_ sai_object_id_t port_id)
{
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED;
}

sai_status_t SwitchBCM81724::create_qos_queues()
{
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED;
}

sai_status_t SwitchBCM81724::set_switch_mac_address()
{
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED;
}

sai_status_t SwitchBCM81724::refresh_port_list(
        _In_ const sai_attr_metadata_t *meta)
{
    SWSS_LOG_ENTER();

    // since now port can be added or removed, we need to update port list
    // dynamically

    sai_attribute_t attr;

    m_port_list.clear();

    // iterate via ASIC state to find all the ports

    for (const auto& it: m_objectHash.at(SAI_OBJECT_TYPE_PORT))
    {
        sai_object_id_t port_id;
        sai_deserialize_object_id(it.first, port_id);

        m_port_list.push_back(port_id);
    }

    uint32_t port_count = (uint32_t)m_port_list.size();

    attr.id = SAI_SWITCH_ATTR_PORT_LIST;
    attr.value.objlist.count = port_count;
    attr.value.objlist.list = m_port_list.data();

    CHECK_STATUS(set(SAI_OBJECT_TYPE_SWITCH, m_switch_id, &attr));

    attr.id = SAI_SWITCH_ATTR_PORT_NUMBER;
    attr.value.u32 = port_count;

    CHECK_STATUS(set(SAI_OBJECT_TYPE_SWITCH, m_switch_id, &attr));

    SWSS_LOG_NOTICE("refreshed port list, current port number: %zu, not counting cpu port", m_port_list.size());

    return SAI_STATUS_SUCCESS;
}

sai_status_t SwitchBCM81724::set_switch_default_attributes()
{
    SWSS_LOG_ENTER();

    sai_status_t ret;

    // Fill this with supported SAI_OBJECT_TYPEs
    int32_t supported_obj_list[] = {
                                SAI_OBJECT_TYPE_NULL,
                                SAI_OBJECT_TYPE_PORT
                              }; 
    SWSS_LOG_INFO("create switch default attributes");

    sai_attribute_t attr;

    attr.id = SAI_SWITCH_ATTR_NUMBER_OF_ACTIVE_PORTS;
    attr.value.u32 = 0;

    CHECK_STATUS(set(SAI_OBJECT_TYPE_SWITCH, m_switch_id, &attr));

    attr.id = SAI_SWITCH_ATTR_WARM_RECOVER;
    attr.value.booldata = false;

    CHECK_STATUS(set(SAI_OBJECT_TYPE_SWITCH, m_switch_id, &attr));

    attr.id = SAI_SWITCH_ATTR_TYPE;
    attr.value.s32 = SAI_SWITCH_TYPE_PHY;

    CHECK_STATUS(set(SAI_OBJECT_TYPE_SWITCH, m_switch_id, &attr));

    attr.id = SAI_SWITCH_ATTR_FIRMWARE_MAJOR_VERSION;
    strncpy((char *)&attr.value.chardata, "v0.1", sizeof(attr.value.chardata));

    CHECK_STATUS(set(SAI_OBJECT_TYPE_SWITCH, m_switch_id, &attr));

    attr.id = SAI_SWITCH_ATTR_SUPPORTED_OBJECT_TYPE_LIST;
    attr.value.s32list.count = sizeof(supported_obj_list);
    attr.value.s32list.list = supported_obj_list;

    ret = set(SAI_OBJECT_TYPE_SWITCH, m_switch_id, &attr);

    return ret;
}

SwitchBCM81724::SwitchBCM81724(
        _In_ sai_object_id_t switch_id,
        _In_ std::shared_ptr<RealObjectIdManager> manager,
        _In_ std::shared_ptr<SwitchConfig> config,
        _In_ std::shared_ptr<WarmBootState> warmBootState):
    SwitchStateBase(switch_id, manager, config, warmBootState)
{
    SWSS_LOG_ENTER();

    // empty
}

// override of base class but returning failure in most cases. GB phys implement very little

sai_status_t SwitchBCM81724::refresh_read_only(
        _In_ const sai_attr_metadata_t *meta,
        _In_ sai_object_id_t object_id)
{
    SWSS_LOG_ENTER();

    if (meta->objecttype == SAI_OBJECT_TYPE_SWITCH)
    {
        switch (meta->attrid)
        {
            case SAI_SWITCH_ATTR_CPU_PORT:
            case SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID:
            case SAI_SWITCH_ATTR_DEFAULT_VLAN_ID:
            case SAI_SWITCH_ATTR_DEFAULT_STP_INST_ID:
            case SAI_SWITCH_ATTR_DEFAULT_1Q_BRIDGE_ID:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_SWITCH_ATTR_ACL_ENTRY_MINIMUM_PRIORITY:
            case SAI_SWITCH_ATTR_ACL_ENTRY_MAXIMUM_PRIORITY:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_SWITCH_ATTR_MAX_ACL_ACTION_COUNT:
            case SAI_SWITCH_ATTR_ACL_STAGE_INGRESS:
            case SAI_SWITCH_ATTR_ACL_STAGE_EGRESS:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_SWITCH_ATTR_NUMBER_OF_ECMP_GROUPS:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_SWITCH_ATTR_NUMBER_OF_ACTIVE_PORTS:
            case SAI_SWITCH_ATTR_PORT_LIST:
                return refresh_port_list(meta);

            case SAI_SWITCH_ATTR_QOS_MAX_NUMBER_OF_CHILDS_PER_SCHEDULER_GROUP:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY:
            case SAI_SWITCH_ATTR_AVAILABLE_DNAT_ENTRY:
            case SAI_SWITCH_ATTR_AVAILABLE_DOUBLE_NAT_ENTRY:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_SWITCH_ATTR_DEFAULT_TRAP_GROUP:
            case SAI_SWITCH_ATTR_FIRMWARE_MAJOR_VERSION:
                return SAI_STATUS_SUCCESS;
        }
    }

    if (meta->objecttype == SAI_OBJECT_TYPE_PORT)
    {
        switch (meta->attrid)
        {
            case SAI_PORT_ATTR_QOS_NUMBER_OF_QUEUES:
            case SAI_PORT_ATTR_QOS_QUEUE_LIST:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_PORT_ATTR_NUMBER_OF_INGRESS_PRIORITY_GROUPS:
            case SAI_PORT_ATTR_INGRESS_PRIORITY_GROUP_LIST:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_PORT_ATTR_QOS_NUMBER_OF_SCHEDULER_GROUPS:
            case SAI_PORT_ATTR_QOS_SCHEDULER_GROUP_LIST:
                return SAI_STATUS_NOT_IMPLEMENTED;

            case SAI_PORT_ATTR_SUPPORTED_FEC_MODE:
            case SAI_PORT_ATTR_SUPPORTED_AUTO_NEG_MODE:
            case SAI_PORT_ATTR_REMOTE_ADVERTISED_FEC_MODE:
            case SAI_PORT_ATTR_ADVERTISED_FEC_MODE:
                return SAI_STATUS_SUCCESS;

                /*
                 * This status is based on hostif vEthernetX status.
                 */

            case SAI_PORT_ATTR_OPER_STATUS:
                return SAI_STATUS_SUCCESS;
        }
    }

    if (meta->objecttype == SAI_OBJECT_TYPE_SCHEDULER_GROUP)
    {
        return SAI_STATUS_NOT_IMPLEMENTED;
    }

    if (meta->objecttype == SAI_OBJECT_TYPE_BRIDGE && meta->attrid == SAI_BRIDGE_ATTR_PORT_LIST)
    {
        return SAI_STATUS_NOT_IMPLEMENTED;
    }

    if (meta->objecttype == SAI_OBJECT_TYPE_VLAN && meta->attrid == SAI_VLAN_ATTR_MEMBER_LIST)
    {
        return SAI_STATUS_NOT_IMPLEMENTED;
    }

    if (meta->objecttype == SAI_OBJECT_TYPE_DEBUG_COUNTER && meta->attrid == SAI_DEBUG_COUNTER_ATTR_INDEX)
    {
        return SAI_STATUS_SUCCESS;  // XXX not sure for gearbox
    }

    auto mmeta = m_meta.lock();

    if (mmeta)
    {
        if (mmeta->meta_unittests_enabled())
        {
            SWSS_LOG_NOTICE("unittests enabled, SET could be performed on %s, not recalculating", meta->attridname);

            return SAI_STATUS_SUCCESS;
        }
    }
    else
    {
        SWSS_LOG_WARN("meta pointer expired");
    }

    SWSS_LOG_WARN("need to recalculate RO: %s", meta->attridname);

    return SAI_STATUS_NOT_IMPLEMENTED;
}

sai_status_t SwitchBCM81724::create_default_vlan() 
{   
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::create_cpu_port() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::create_default_1q_bridge() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::create_ports() 
{
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
} 

sai_status_t SwitchBCM81724::create_default_virtual_router() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::create_default_stp_instance() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::create_default_trap_group() 
{ 
    SWSS_LOG_ENTER();

    SWSS_LOG_INFO("create default trap group");

    sai_attribute_t attr;

    attr.id = SAI_SWITCH_ATTR_DEFAULT_TRAP_GROUP;
    attr.value.oid = SAI_NULL_OBJECT_ID;

    return set(SAI_OBJECT_TYPE_SWITCH, m_switch_id, &attr);
}

sai_status_t SwitchBCM81724::create_ingress_priority_groups_per_port(
                    _In_ sai_object_id_t port_id) 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::create_ingress_priority_groups() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::create_vlan_members() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::create_bridge_ports() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::set_acl_entry_min_prio() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::set_acl_capabilities() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::set_maximum_number_of_childs_per_scheduler_group() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::set_number_of_ecmp_groups() 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
} 

sai_status_t SwitchBCM81724::refresh_bridge_port_list(
                    _In_ const sai_attr_metadata_t *meta,
                    _In_ sai_object_id_t bridge_id) 
{ 
    SWSS_LOG_ENTER();

    return SAI_STATUS_NOT_IMPLEMENTED; 
}

sai_status_t SwitchBCM81724::refresh_vlan_member_list(
                    _In_ const sai_attr_metadata_t *meta,
                    _In_ sai_object_id_t vlan_id) 
{ 
    SWSS_LOG_ENTER();
    
    return SAI_STATUS_NOT_IMPLEMENTED; 
}

